/**
 *
 */
package siebes.rmi.sorter;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.List;


/**
 * @author eddie
 *
 */
public final class SortClient {


    /**
     * geen public constructor nodig ivm main class.
     */
    private SortClient() {
    }
    /**
     * maximale grootte van sorteerarray.
     */
    static final int MAXLISTSIZE = 100;

    /**
     * max grootte binnen de ascii tabel.
     */
    static final int MAXASCIIVAL = 256;

    /**
     * Type klasse dat gesorteerd gaat worden.
     */
    @SuppressWarnings("rawtypes")
    static final Class KLASSE = Integer.class;
    /**
     * Aantal processen (threads) die aan de sorteeropgave worden
     * meegegeven.
     */
    static final int NUMBEROFPROCS = 1;

    /**
     * Placeholder.
     */
    private static SortClient sortclient;

    /**
     * Verdeelt een lijst in een aantal bereiken.
     * Gebaseerd op het aantal processen.
     * @param t klasse type die meegegeven wordt.
     * @param numberOfProcesses aantal processen die draaien.
     * @return aantal ranges.
     */
    @SuppressWarnings("rawtypes")
    private  int[] getRanges(final int numberOfProcesses, final Class t) {
        int[] ranges = new int[numberOfProcesses];
        for (int i = 0; i < numberOfProcesses; i++) {
            if (i > 0) {
                double tempRange = 0.000;
                if (t == Integer.class) {
                    tempRange =
                        ((1.000 * i) / numberOfProcesses) * MAXLISTSIZE;
                } else if (t == String.class) {
                    tempRange =
                            ((1.000 * i) / numberOfProcesses) * MAXASCIIVAL;
                }
                ranges[i] = (int) tempRange;
            } else {
                ranges[i] = 0;
            }
        }
        return ranges;
    }

    /**
     * Maak van comparable list een array.
     * @param list : lijst comparables
     * @return array comparables
     */
    @SuppressWarnings("rawtypes")
    private Comparable[] listToComparableArray(final List<Comparable> list) {
        Comparable[] comparable = new Comparable[list.size()];
        for (int i = 0; i < list.size(); i++) {
            comparable[i] = list.get(i);
        }
        return  comparable;
    }

    /**
     * @param numberOfProcesses aantal threads die
     * voor berekening nodig zijn.
     * @return 2 dimensionale array op basis input..
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    private List<Comparable>[] createListArray(final int numberOfProcesses) {
        List[] tempList = new List[numberOfProcesses];
        for (int i = 0; i < tempList.length; i++) {
            tempList[i] = new ArrayList();
        }
        return tempList;
    }

    /**
     * @param list Ivoer lijst met random objecten.
     * @param numberOfProcesses Aantal threads die draaien.
     * @param klasse type objecten die gesplit gaan worden.
     * @return Een 2 dimensionale array met items die binnen
     * een opgegeven range vallen.
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    private Comparable[][] spliceList(
            final Comparable[] list,
            final int numberOfProcesses,
            final Class klasse) {
        List[] lists = createListArray(numberOfProcesses);
        int[] ranges = getRanges(numberOfProcesses, klasse);
        //ga bij elk element in list na in welke range deze hoort
        //Stop deze daarop in de juiste array.
        for (int j = list.length - 1; j >= 0; j--) {
            for (int k = ranges.length - 1; k >= 0; k--) {
                if (klasse ==  Integer.class) {
                    if (list[j].compareTo(ranges[k]) >= 0) {
                        lists[k].add(list[j]);
                        break;
                    }
                } else if (klasse == String.class) {
                    if (list[j].compareTo(getAsciiCharFromNr(ranges[k])) >= 0) {
                        lists[k].add(list[j]);
                        break;
                    }
                }
            }
        }
        Comparable[][] splitComparables = new Comparable[lists.length][];
        for (int l = 0; l < lists.length; l++) {
            splitComparables[l] = listToComparableArray(lists[l]);
        }
        return splitComparables;
    }

    /**
     * @param value integer die het nummer van de asci
     * tabel representeerd.
     * @return 1 character.
     */
    private String getAsciiCharFromNr(final int value) {
        return String.valueOf(Character.toChars(value));
    }
    /**
     * @param args k
     * @throws NotBoundException ivm rmi
     * @throws RemoteException ivm rmi
     * @throws MalformedURLException ivm rmi
     */
    public static void main(final String[] args)
            throws MalformedURLException, RemoteException, NotBoundException {
        // TODO Auto-generated method stub
        sortclient = new SortClient();
        sortclient.sort();
    }


    /**
     * Print een gesorteerde lijst.
     * @throws MalformedURLException ivm objecten die over de lijn gaan.
     * @throws RemoteException ivm rmi
     * @throws NotBoundException ivm rmi
     */
    @SuppressWarnings("rawtypes")
    private void sort()
            throws MalformedURLException, RemoteException, NotBoundException {
        ComparableSupplier compSup = new ComparableSupplier();
        Comparable[] intList =
                compSup.generateRandomList(MAXLISTSIZE, KLASSE);
        ISortFactory factory =
                (ISortFactory) Naming.lookup("//localhost:1100/factory");
        Comparable[][] test = spliceList(intList, NUMBEROFPROCS, KLASSE);
        ListJoiner joiner = new ListJoiner(NUMBEROFPROCS);
        for (int i = 0; i < test.length; i++) {
            SorterThread sorterThread = new SorterThread(factory, test[i]);
            sorterThread.setThreadNumber(i);
            sorterThread.addObserver(joiner);
            Thread thread = new Thread(sorterThread);
            thread.start();
        }
    }
}
